<?php

declare(strict_types=1);

namespace App\Controller\Chat\Ws;

use App\Constants\Chat\RedisKey;
use App\Controller\AbstractController;
use App\Foundation\Facades\Log;
use App\Foundation\Facades\MessageParser;
use App\Pool\Redis;

use App\Service\Chat\ChatService;
use App\Service\SenderService;
use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\HttpServer\Router\DispatcherFactory;
use App\Foundation\Utils\JWT;
use Swoole\Http\Request;
use Swoole\Websocket\Frame;
use Swoole\WebSocket\Server;
use Hyperf\WebSocketServer\Context;

/**
 * 聊天
 * Class WebsocketController.
 */
class WebsocketController extends AbstractController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{

    /**
     * @Inject
     * @var JWT
     */
    private $jwt;

    /**
     * 用户发送信息.
     * @param \Swoole\Http\Response|Server $server
     * @param Frame $frame
     */
    public function onMessage($server, Frame $frame): void
    {
        //Log::debug()->info('onMessage:' . var_export(Context::get('user_info'), true));
        $userInfo = Context::get('user_info');
        //获取用户信息
        if (empty($userInfo)) {
            $this->closeClient($server, $frame->fd, '3333');
            return;
        }
        $data = json_decode($frame->data, true);
        print_r($data);
        //路由处理
        $targetUri = $data['uri'] ?? '';
        $requestMethod = $data['method'] ?? 'GET';
        $payload = $data['payload'] ?? [];
        $dispatcher = $this->container
            ->get(DispatcherFactory::class)
            ->getDispatcher('ws');
        $dispatched = make(Dispatched::class, [
            $dispatcher->dispatch($requestMethod, $targetUri),
        ]);
        if (!$dispatched->isFound()) {
            $this->debug($frame->fd, 'uri not found : ' . $targetUri);
            return;
        }
        Context::set('fd', $frame->fd);
        try {
            $class = $dispatched->handler->callback[0];
            $method = strval($dispatched->handler->callback[1]);
            //$this->debug($frame->fd, 'call : ' . $class . '@' . $method);
            $result = call_user_func_array([make($class), $method], $payload);
        } catch (\Throwable $throwable) {
            $this->debug($frame->fd, $throwable->getMessage() . $throwable->getTraceAsString());
        }
        if (empty($result)) {
            return;
        }
        foreach ((array)$result as $item) {
            if (!empty($item['user_id'])) {
                SenderService::getInstance()->sendToUid($item['user_id'], $item['cmd'], $item['payload']);
            } else {
                $item['fd'] && SenderService::getInstance()->sendToFd(intval($item['fd']), $item['cmd'], $item['payload']);
            }
        }
    }

    /**
     * 用户连接服务器.
     * @param \Swoole\Http\Response|Server $server
     * @param Request $request
     */
    public function onOpen($server, Request $request): void
    {
        $userInfo = Context::get('user_info');
        Context::set('test', 'test');
        //获取用户信息
        if (empty($userInfo)) {
            $this->closeClient($server, $request->fd, 'onOpen user error');
            return;
        }
        //将在线用户放置Redis中
        Redis::getInstance()->hSet(RedisKey::ONLINE_USER_ID_KEY, (string)$userInfo['id'], (string)$request->fd);
        //将FD对应在线用户ID放置Redis中
        Redis::getInstance()->hSet(RedisKey::ONLINE_USER_FD_KEY, (string)$request->fd, (string)$userInfo['id']);
    }

    /**
     * 用户关闭连接.
     * @param \Swoole\Http\Response|Server $server
     * @param int $fd
     * @param int $reactorId
     */
    public function onClose($server, int $fd, int $reactorId): void
    {
        //删除在线列表中的用户
        Redis::getInstance()->hDel(RedisKey::ONLINE_USER_FD_KEY, (string)$fd);
        $uid = ChatService::getUidByFd((string)$fd);
        if (!$uid) {
            return;
        }
        Redis::getInstance()->hDel(RedisKey::ONLINE_USER_ID_KEY, (string)$uid);
    }

    /**
     * @param \Swoole\Http\Response|Server $server
     * @param int $fd
     * @param string $msg
     * @param string $cmd
     */
    private function closeClient($server, $fd, $msg = '', $cmd = 'tokenError')
    {
        $data = [
            'cmd' => $cmd,
            'data' => $msg
        ];
        $server->push($fd, json_encode($data, JSON_UNESCAPED_UNICODE));
        $server->disconnect($fd);
    }

    protected function debug($fd, $msg)
    {
        SenderService::getInstance()->debug($fd, $msg);
    }
}
